package sma.organizations.roles;

import javax.vecmath.Vector2d;

import org.janusproject.kernel.agentmemory.Memory;
import org.janusproject.kernel.agentsignal.QueuedSignalAdapter;
import org.janusproject.kernel.crio.core.Role;
import org.janusproject.kernel.message.Message;
import org.janusproject.kernel.message.StringMessage;
import org.janusproject.kernel.status.Status;
import org.janusproject.kernel.status.StatusFactory;
import sma.agents.bodies.CircleBody;
import sma.environment.LocalEnvironment;
import sma.organizations.messages.GoalMessage;
import sma.organizations.signals.SendMoveGoalSignal;
import sma.organizations.signals.SendMoveSignal;
import appli.utils.Config;
import appli.utils.MoveEnum;

/**
 * Goal est le role tenu par un GoalAgent qui a pour but de definir un es
 * objectifs du vehicule.
 * 
 * Depuis la verison 0.3 nous utilisons le groupe de l'agent.
 * Depuis la version 0.2 nous communiquons via des signaux et des channels
 * 
 * @author Etienne Sainton
 * @version 0.2
 */
public class Goal extends Role {
	/**
	 * Sa coordonnee courante en X
	 */
	private int currentDivX;

	/**
	 * Sa coordonnee courante en Y
	 */
	private int currentDivY;

	/**
	 * 
	 * Son environnement local
	 * 
	 */
	private sma.environment.LocalEnvironment environment;

	/**
	 * 
	 * Son body
	 * 
	 */
	private sma.agents.bodies.CircleBody body;
	
	/**
	 * Son groupe
	 */
	private int groupe;
	
	/**
	 * Create a local listener on the signals coming from the agent itself.
	 */
	private QueuedSignalAdapter<SendMoveGoalSignal> privateMoveSignals = new QueuedSignalAdapter<SendMoveGoalSignal>(SendMoveGoalSignal.class);

	/**
	 * Le mouvement qu'il va devoir effectuer au prochain cycle de vie
	 */
	public static final String MOVE = "NONE";

	/**
	 * Cette inner class Enum, eprmet de lister les états de la vie de l'agent.
	 * Il y'en a 2 : I_M_HERE et MOVING
	 * @author Etienne Sainton
	 * @since 0.1
	 */
	public enum State {
		/**
		 * Envoie de sa position aux agents
		 */
		I_M_HERE, 
		 /**
		 * Deplacement
		 */
		MOVING,
		;
	}

	/**
	 * 
	 * L'etat courant de l'agent
	 * 
	 */
	private Goal.State currentState = State.I_M_HERE;

	/**
	 * Initialisation de l'agent. Automatiquement appelee par le constructeur
	 * 
	 * @param _environment l'environnement dans lequel évolue l'agent
	 * @param _body la représentation graphique de l'agent
	 */
	private void init(sma.environment.LocalEnvironment _environment,
			sma.agents.bodies.CircleBody _body, int _groupe) {
		this.environment = _environment;
		this.groupe = _groupe;
		this.body = _body;
		this.body.setGroupe(this.groupe);
		this.currentDivX = Config.PANEL_SIZE - body.getDiameter() / 2;
		this.currentDivY = Config.PANEL_SIZE - body.getDiameter() * 2;
	}

	/**
	 * Override de l'activate defini par Janus. Nous en profitons pour lui
	 * affecter son environnement et son body.
	 */
	@Override
	public Status activate(Object... params) {
		init((LocalEnvironment) params[0], (CircleBody) params[1], (Integer) params[2]);
		getSignalEmitter().addSignalListener(this.privateMoveSignals);
		if (Config.DEBUG == true)
			System.out.println("Le role Goal est initialisé");

		return StatusFactory.ok(this);
	}

	/**
	 * Override du live defini par Janus. Le raisonnement est le suivant : a
	 * chaque iteration je regarde els mouvements que je dois effectuer, puis je
	 * recalcule ma position.
	 */
	@Override
	public Status live() {
		run();
		return StatusFactory.ok(this);
	}

	private void run() {
		switch (this.currentState) {
		case I_M_HERE:
			// Je réponds à tous ceux qui m'ont demandé ma position :
			for (Message msg : getMailbox())
				sendMessage(Decisive.class, msg.getSender(), new GoalMessage(this.groupe,
						this.body.getPosition()));

			// Et ensuite on va bouger :
			this.currentState = State.MOVING;
		case MOVING:
			// Le mouvement à appliquer :
			Vector2d direction = new Vector2d();

			// On récupère ça :
			move();

			// Et on bouge :
			direction.x = currentDivX;
			direction.y = currentDivY;
			this.body.setPosition(direction);

			// Et maintenant on va dire ou on se trouve
			this.currentState = State.I_M_HERE;
		}
	}

	/**
	 * Cette fonction permet de recuperer le dernier mouvement envoye au
	 * clavier. Ensuite elle calcul la prochaine position en fonction de la
	 * direction et de la finesse du terrain specifiee dans le fichier config.
	 * La mouvement sera applique dans le live (qui appelle cette focntion
	 * d'ailleurs).
	 */
	private void move() {
		boolean test = false;
		// Recuperons les derniers mouvements en memoire
		{
			SendMoveGoalSignal sig;
			sig = this.privateMoveSignals.getFirstAvailableSignal();
			while (sig != null) {
				if(sig.getGroupe() == groupe){
					test = true;
					MoveEnum move = sig.getMove();
					// Evitons nous une batterie de test si jamais on n'a rien reçu
					if (move != null) {
						// Ce code est tiré de la version précédente de l'application
						//Quick&dirty methode pour que les goals se balladent sur les deux panels
						if(groupe == 6500){
							if (move == MoveEnum.UP)
								currentDivY = Math.max(0, currentDivY
										- (environment.getHeight() / Config.RESOLUTION));
							else if (move == MoveEnum.DOWN)
								currentDivY = Math.min(environment.getHeight() + Config.PANEL_SIZE + 140, currentDivY
										+ (environment.getHeight() / Config.RESOLUTION));
							else if (move == MoveEnum.LEFT)
								currentDivX = Math.max(0, currentDivX
										- (environment.getWidth() / Config.RESOLUTION));
							else if (move == MoveEnum.RIGHT)
								currentDivX = Math.min(environment.getWidth(), currentDivX
										+ (environment.getWidth() / Config.RESOLUTION));	
						}else{
							if (move == MoveEnum.UP)
								currentDivY = Math.max(-Config.PANEL_SIZE - 140, currentDivY
										- (environment.getHeight() / Config.RESOLUTION));
							else if (move == MoveEnum.DOWN)
								currentDivY = Math.min(environment.getHeight(), currentDivY
										+ (environment.getHeight() / Config.RESOLUTION));
							else if (move == MoveEnum.LEFT)
								currentDivX = Math.max(0, currentDivX
										- (environment.getWidth() / Config.RESOLUTION));
							else if (move == MoveEnum.RIGHT)
								currentDivX = Math.min(environment.getWidth(), currentDivX
										+ (environment.getWidth() / Config.RESOLUTION));
						}
						
					}
				}
				sig = this.privateMoveSignals.getFirstAvailableSignal();
			}
		}
		
		if(test == true){
			fireSignal(new SendMoveSignal(this));
		}
	}
}
